#!/usr/bin/env python
# -*- coding:utf-8 -*-
import SocketServer
from conf import settings
import json
import sys
import os
import hashlib
import time

#8、定义多线程socket服务与连接交互handle函数
class MyTcpHandle(SocketServer.BaseRequestHandler):
    print "\033[32;1mmyserver start on %s:%s......\n\033[0m" % (settings.host,settings.port)   #带颜色的启动信息
    response_code_list = {
        '200': "Pass authentication!",
        '201': "Wrong username or password",
        '202': "Invalid username or password",
        '300': "Ready to send file to client",
        '301': "Client ready to receive file data ",
        '302': "File doesn't exist",
        '303': "Continue download",
    }

    def handle(self):
        print "connect success：",self.client_address  #连接成功打印客户连接地址和端口
        while True:
            data=self.request.recv(1024)
            print data
            if not data:
                print "\033[31;1mConnect disconnected\033[0m", self.client_address
                break
            self.client_instruction(data)

    #定义一个用户指令解析器，通过指令触发相应的函数,包括登录，下载，上传等；
    def client_instruction(self,instructions):   #客户指令
        instructions=instructions.split("|")
        func_str=instructions[0]    #第一个参数是指令，下面要定义这个方法；
        if hasattr(self,func_str):
            func=getattr(self,func_str)
            func(instructions)      #指令列表传给调用的方法
        else:
            print "\033[31;1m command %s invalid\033[0m"%func_str

    def login(self,args):
        ins_info=json.loads(args[1])
        self.username=ins_info['username']
        if ins_info['username'] in settings.USER_ACCOUNT:   #用户存在
            if ins_info['password']==settings.USER_ACCOUNT[self.username]['password']:
                response_code='200' #认证成功，返回200状态码
                self.login_user=ins_info['username']    #用这个去判断是否登录成功
            else:
                response_code='201'
        else:
            response_code='202' #用户不存在，状态码设定为202
        #整合要发送给客户端的字符串
        response_str="response|%s"% response_code    #两边的状态码约定一样就不用发送错误的详细信息了
        self.request.send(response_str)     #发给客户端
        # return response_code

    def download(self,data):
        if not self.login_user: #用户认证检查
            self.request.send("User not logged in.")
            return
        print "download instruction."
        file_name_list=json.loads(data[1])
        for file_name in file_name_list:
            file_path=os.path.join(settings.home_dir,self.login_user,file_name)
            print file_path
            if os.path.isfile(file_path):   #传输完整文件
                file_size=os.path.getsize(file_path)
                send_data="300|%s|%s"%(file_size,self.file_md5())
                self.request.send(send_data)
                res_data_list=self.request.recv(1024).split("|")
                if res_data_list[0]=="301":
                    sent_size = 0
                    file_md5 = hashlib.md5()
                    t_start = time.time()
                    with open(file_path,"rb")as f:
                        while sent_size != file_size:
                            data=f.read(4096)
                            self.request.send(data)
                            sent_size += len(data)
                            print("send:",file_size,sent_size)
                            self.request.recv(1024)     #防止粘包，接收用户发的：“MD5？”；
                            file_md5.update(data)  #可以边传输边进行md5校验,updata更新data的md5值；
                            md5_str = file_md5.hexdigest()  #MD5的最终结果
                            self.request.send(md5_str)
                            msg=self.request.recv(1024)
                            if msg=="no":
                                print "md5对不上，传输失败"
                                break
                        else:
                            cost_time = time.time() - t_start
                            print cost_time
                elif res_data_list[0]=="303":   #断点续传
                    start_point=int(res_data_list[1])
                    sent_size=start_point   #不是从0开始发了
                    file_md5 = hashlib.md5()
                    with open(file_path,"rb")as f:
                        while sent_size != file_size:   #循环传输文件
                            f.seek(start_point)
                            data=f.read(4096)
                            self.request.send(data)
                            sent_size += len(data)
                            file_md5.update(data)   #更新MD5
                            print("send:",file_size,sent_size)
                        else:
                            self.request.recv(1024) #接收用户的MD5询问，防止粘包
                            md5_str = file_md5.hexdigest()
                            self.request.send(md5_str)

                else:   #文件不存在
                    self.request.send("302")

    def file_md5(self):
        pass    #以前是算文件总体的传过去，但如果传输文件太大，效率较低，采用在代码中边传输边计算MD5值；

    def upload(self,data):
        if not self.login_user: #用户认证检查
            self.request.send("User not logged in.")
            return
        else:
            self.request.send("comm received.")
        print "download instruction."
        file_name_list=json.loads(data[1])
        for file_name in file_name_list:
            file_path=os.path.join("f:\\",self.login_user,file_name)
            file_attr=self.request.recv(1024)     #接收上传的“filename|filesize”
            file_attr_list=file_attr.split("|")     #接收的数据切成列表
            if file_name != file_attr_list[0]:continue
            print file_attr_list
            file_size=int(file_attr_list[1])     #总文件大小
            print file_size
            self.request.send("start")
            with open(file_path,"wb")as f:
                recv_size=0
                while recv_size != file_size:
                    data=self.request.recv(4096)
                    recv_size+=len(data)
                    f.write(data)
                    print("recv size:", file_size,recv_size)
                else:
                    print "upload success."

    def listfile(self,data):
        if not self.login_user: #用户认证检查
            self.request.send("User not logged in.")
            return
        else:
            cwd=data[1]
            file_list=os.listdir(cwd)
            self.request.send(json.dumps(file_list))

    def chrdir(self,data):
        if not self.login_user: #用户认证检查
            self.request.send("User not logged in.")
            return
        else:
            to_dir=data[1]
            os.chdir(to_dir)
            cwd=os.getcwd()
            self.request.send(cwd)
    # def exit(self,actions):
    #     print "\033[31;1mConnect disconnected\033[0m", self.client_address